/**
 * Class Jsl.HtmlCollection - HTMLElements collection implementation
 * 
 * @author  Mykhailo Stadnyk <mikhus@gmail.com>
 * @package Jsl
 * @version 1.0
 */
Jsl.Collection = Jsl.extend( Object, function() {

	return {
	/**
	 * Public members
	 */

	/**
	 * Total length of an elements in collection
	 * 
	 * @type {Integer}
	 * @readonly
	 */
	length : 0,
	
	/**
	 * DOM elements
	 * 
	 * @type {Array}
	 * @readonly
	 */
	tags : [],

	/**
	 * Class constructor
	 * 
	 * @param {Array|NodeList|HTMLCollection} tags
	 */
	constructor : function( tags) {
		this.tags   = Jsl.arrize( tags);
		this.length = this.tags.length;

		Jsl.Collection.$super.constructor.call( this);
	},

	/**
	 * Iterating the elements of collection calls a user-defined function
	 * on each iteration
	 * 
	 * User-function will be called with 2 arguments:
	 *  @param {HTMLElement} - element
	 *  @param {Integer}     - element index inside collection
	 *  
	 * 
	 * @param {Finction} fn  - function to call
	 * @param {Object} scope - scope to bind a function
	 */
	each : function( fn, scope) {
		var el    = this.tags,
			scope = scope || el
		;
		Jsl.each( el, fn, scope);
		return this;
	},

	/**
	 * Adds an element to collection
	 * 
	 * @param  {HTMLElement} el     - element to add
	 * @return {Jsl.Collection} - this collection
	 */
	add : function( el) {
		if (Jsl.isElement( el)) {
			this.tags[this.tags.length] = el;
			this.length++;
		}
		return this;
	},

	/**
	 * Removes an element from this collection by
	 * specifying en element index or an element itself
	 * 
	 * @param  {Integer|HTMLElement} el
	 * @return {Js.Collection}
	 */
	del : function( el) {
		if (Jsl.isElement( el)) {
			this.each( function( _el, indx) {
				if (_el == el) {
					this.tags.splice( indx, 1);
					this.length--;
					return false;
				}
			});
		} else {
			if (this.tags[el]) {
				this.tags.splice( el, 1);
				this.length--;
			}
		}
		return this;
	},

	/**
	 * Filters current collection by a given selector
	 * and returns new collection of matching elements
	 * 
	 * @return {Jsl.Collection}
	 */
	filter : function( selector) {
		return new Jsl.Collection(
			Sizzle.matches( String( selector), this.tags)
		);
	},
	
	/**
	 * Removes current node from the DOM tree
	 */
	remove : function() {
		var r = this;
		this.each( function( el) {
			Jsl.freeElement( el);
			el.parentNode.removeChild( el);
		});
		return r;
	},

	/**
	 * Filters current collection by a given selector
	 * and returns new collection of elements NOT matching the
	 * given selector.
	 * 
	 * NOTE: for this expression ONLY simple selectors are allowed
	 * 
	 * @return {Jsl.Collection}
	 */
	not : function( selector) {
		return new Jsl.Collection(
			Sizzle.filter( String( selector), this.tags, false, true)
		);
	},

	/**
	 * Returns an element from this collection by it's index
	 * If no element with given index esists will return null
	 * 
	 * @param  {Integer} indx - element index
	 * @return {HTMLElement}
	 */
	item : function( indx) {
		return this.tags[indx] || null;
	},

	/**
	 * Merges one or more collection with current collection
	 * 
	 * NOTE: if passed argument is not an instance of Jsl.Collection
	 *       it will NOT be merged with current collection
	 * 
	 * @param  {Jsl.Collection} [coll1, coll2, ...]
	 * @return {Jsl.Collection}
	 */
	merge : function() {
		for (var i = 0, s = arguments.length; i < s; i++) {
			var coll = arguments[i];
			if (coll instanceof Jsl.Collection) {
				var self = this;
				coll.each( function( tag) { self.add( tag); });
			}
		}
		return this;
	},

	/**
	 * Returns a total number of elements inside this collection
	 * 
	 * @return {Integer}
	 */
	count : function() {
		return this.length;
	},

	/**
	 * Destroys current collection object
	 */
	destroy : function() {
		var self = this;
		for (var i = 0; i < this.length; i++) {
			delete this.tags[i];
		}
		delete self;
	},

	/**
	 * Returns Array representation of Jsl.Collection object
	 * 
	 * @return {Array}
	 */
	toArray : function() {
		return Jsl.arrize( this.tags);
	}

};}());
